<?php

namespace App\CustHub\Service;

use App\CustHub\Dto\AddContactWayDto;
use App\CustHub\Dto\AddMsgTemplateDto;
use App\CustHub\Interfaces\OpenWorkAccountInterface;
use App\CustHub\Mapper\ChOpenWorkAgentMapper;
use App\CustHub\Utils\OpenWorkAppUtil;
use App\CustHub\Work\AuthorizerAccessToken;
use App\CustHub\Work\OpenWorkAccount;
use Carbon\Carbon;
use EasyWeChat\Kernel\HttpClient\AccessTokenAwareClient;
use EasyWeChat\Kernel\HttpClient\Response;
use EasyWeChat\Kernel\Traits\InteractWithClient;
use EasyWeChat\Kernel\Traits\InteractWithHttpClient;
use EasyWeChat\Kernel\Traits\InteractWithServerRequest;
use EasyWeChat\OpenWork\SuiteAccessToken;
use EasyWeChat\OpenWork\SuiteTicket;
use Hyperf\Config\Config;
use Hyperf\Contract\ConfigInterface;
use Mine\Exception\NormalStatusException;
use Psr\SimpleCache\CacheInterface;
use EasyWeChat\Kernel\Contracts\AccessToken as AccessTokenInterface;
use EasyWeChat\OpenWork\Contracts\SuiteTicket as SuiteTicketInterface;
use function Hyperf\Collection\collect;
use function Hyperf\Support\value;

class OpenWorkService
{

    use InteractWithClient;
    use InteractWithHttpClient;
    use InteractWithServerRequest;

    protected ConfigInterface $config;

    protected CacheInterface $cache;

    protected ?OpenWorkAccountInterface $account = null;

    protected ?AccessTokenInterface $suiteAccessToken = null;

    protected ?SuiteTicketInterface $suiteTicket = null;

    protected mixed $agentId;

    public $errMsg = '';

    public function __construct(mixed $authCorpId)
    {
        $agentInfo = container()->get(ChOpenWorkAgentMapper::class)->getInfoByCorpid($authCorpId);
        $this->agentId = $agentInfo->id;

        $config = container()->get(ConfigInterface::class)->get('wechat.wework');
        $config['corp_id'] = $agentInfo->wx_corpid;
        $config['provider_secret'] = $agentInfo->wx_secret;
        [$suiteId, $suiteSecret] = OpenWorkAppUtil::getSuiteIdAndSuiteSecret();
        $config['suite_id'] = $suiteId;
        $config['suite_secret'] = $suiteSecret;
        $this->config = new Config($config);
        $this->cache = container()->get(CacheInterface::class);
    }

    public function getAccount(): OpenWorkAccount
    {
        if (! $this->account) {
            $this->account = new OpenWorkAccount(
                corpId: (string) $this->config->get('corp_id'), /** @phpstan-ignore-line */
                providerSecret: (string) $this->config->get('provider_secret'), /** @phpstan-ignore-line */
                suiteId: (string) $this->config->get('suite_id'), /** @phpstan-ignore-line */
                suiteSecret: (string) $this->config->get('suite_secret'), /** @phpstan-ignore-line */
            );
        }

        return $this->account;
    }

    public function setAccount(OpenWorkAccountInterface $account): static
    {
        $this->account = $account;
        return $this;
    }

    public function getAgentId()
    {
        return $this->agentId;
    }

    public function createClient(): AccessTokenAwareClient
    {
        return (new AccessTokenAwareClient(
            client: $this->getHttpClient(),
            accessToken: $this->getAuthorizerAccessToken(),
            failureJudge: fn (Response $response) => (bool) ($response->toArray()['errcode'] ?? 0),
            throw: (bool) $this->config->get('http.throw', true),
        ))->setPresets(collect($this->config)->toArray());
    }

    public function getAuthorizerAccessToken(): AuthorizerAccessToken {
        return new AuthorizerAccessToken(
            corpId: $this->getAccount()->getCorpId(),
            corpSecret: $this->getAccount()->getProviderSecret(),
            cache: $this->cache,
            httpClient: $this->getHttpClient(),
        );
    }

    public function getSuiteAccessToken(): AccessTokenInterface
    {
        if (! $this->suiteAccessToken) {

            $this->suiteAccessToken = new SuiteAccessToken(
                suiteId: $this->getAccount()->getSuiteId(),
                suiteSecret: $this->getAccount()->getSuiteSecret(),
                suiteTicket: $this->getSuiteTicket(),
                cache: $this->cache,
                httpClient: $this->getHttpClient(),
            );
        }

        return $this->suiteAccessToken;
    }

    public function getSuiteTicket(): SuiteTicketInterface
    {
        if (! $this->suiteTicket) {
            $this->suiteTicket = new SuiteTicket(
                suiteId: $this->getAccount()->getSuiteId(),
                cache: $this->cache,
                key: 'openWorkSuiteTicket:' . $this->getAccount()->getSuiteId()
            );
        }
        return $this->suiteTicket;
    }

    /**
     * 获取部门列表
     * @param int|null $id
     * @return array|mixed
     */
    public function getDepartmentList(int $id = null): mixed
    {
        $response = $this->requestGet('cgi-bin/department/list',[
            'id' => $id
        ]);
        return $response ? $response['department'] : [];
    }

    public function getUser(string $userid): mixed
    {
        $response = $this->requestGet('cgi-bin/user/get',[
            'userid' => $userid
        ]);
        return $response;
    }

    /**
     * 获取部门成员详情
     * @param int $deptId
     * @return array|mixed
     */
    public function getUserListByDeptId(int $deptId): mixed
    {
        $response = $this->requestGet('cgi-bin/user/list',[
            'department_id' => $deptId,
        ]);
        return $response ? $response['userlist'] : [];
    }

    /**
     * 获取外部联系人列表
     * @param string $userid
     * @return mixed
     */
    public function getExternalContactList(string $userid): mixed
    {
        $response = $this->requestGet('cgi-bin/externalcontact/list',[
            'userid' => $userid,
        ]);
        return $response ? $response['external_userid'] : [];
    }

    public function getAuthInfo(string $authCorpid, string $permanentCode)
    {
        $response = $this->getClient()->request('POST','cgi-bin/service/get_auth_info',[
            'query' => [
                'suite_access_token' => $this->getSuiteAccessToken()
            ],
            'json' => [
                'auth_corpid' => $authCorpid,
                'permanent_code' => $permanentCode
            ]
        ])->toArray(false);
    }

    /**
     * 将企业主体下的明文userid转换为服务商主体下的密文userid。
     * @param array $userid
     * @return mixed
     */
    public function getOpenUserid(array $userid): mixed
    {
        $response = $this->requestPost('cgi-bin/batch/userid_to_openuserid',[
            'userid_list' => $userid
        ]);
        return $response;
    }

    /**
     * openid转userid
     * @param string $openid
     * @return false|mixed
     * @throws \EasyWeChat\Kernel\Exceptions\BadResponseException
     * @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
     */
    public function convert_to_userid(string $openid): mixed
    {
        $response = $this->getClient()->request('POST','/cgi-bin/user/convert_to_userid', [
            'json' => [
                'openid' => $openid
            ]
        ])->toArray(false);
        if($response['errcode'] != 0){
            return false;
        }

        return $response['userid'];
    }

    /**
     * 批量获取客户详情
     * @param array $userIdList
     * @param int $limit
     * @param string $cursor
     * @return array
     */
    public function batchGetExternalContactDetail(array $userIdList, int $limit = 50, string $cursor = ''): array
    {
        $body = [
            'userid_list' => $userIdList,
            'limit' => $limit,
        ];

        !empty($cursor) && $body['cursor'] = $cursor;
        $response = $this->requestPost('cgi-bin/externalcontact/batch/get_by_user',$body);
        return [
            'external_contact_list' => $response['external_contact_list'],
            'next_cursor' => $response['next_cursor']
        ];
    }

    public function getExternalContactDetail(string $userId, string $nextCursor = ''): array
    {
        $query = [
            'external_userid' => $userId,
        ];
        !empty($nextCursor) && $query['cursor'] = $nextCursor;

        $response = $this->getClient()->request('GET','/cgi-bin/externalcontact/get', [
            'query' => $query
        ])->toArray(false);
        if($response['errcode'] != 0){
            logger()->info('客户详情'.$userId.'详情:'.json_encode($response));
            return [];
        }

        return [
            'external_contact' => $response['external_contact'],
            'follow_user' => $response['follow_user'],
            'next_cursor' => $response['next_cursor'] ?? ''
        ];
    }

    /**
     * unionid与external_userid的关联
     * @param string $unionid
     * @param string $openid
     * @param int $subjectType
     * @return array
     * @throws \EasyWeChat\Kernel\Exceptions\BadResponseException
     * @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
     */
    public function unionidToExternalUserid(string $unionid,string $openid,int $subjectType = 0): array
    {
        $body = [
            'unionid' => $unionid,
            'openid' => $openid,
            'subject_type' => $subjectType
        ];

        $response = $this->getClient()->request('POST','cgi-bin/idconvert/unionid_to_external_userid',[
            'json' => $body
        ])->toArray(false);
        logger()->info('好友检测：'.json_encode($body));
        logger()->info('好友检测：'.json_encode($response,JSON_UNESCAPED_UNICODE));
        if($response['errcode'] != 0){
            return [];
        }
        if(isset($response['external_userid'])){
            $isCustomer = true;
            $externalUserid = $response['external_userid'];
        }else{
            //该微信账号尚未成为企业客户时，有效期为90天
            $isCustomer = false;
            $externalUserid = $response['pending_id'];
        }

        return [
            'isCustomer' => $isCustomer,
            'externalUserid' => $externalUserid
        ];
    }

    /**
     * @param array $externalUserid
     * @param string $chatId
     * @return array|mixed
     * @throws \EasyWeChat\Kernel\Exceptions\BadResponseException
     * @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
     */
    public function externalUseridToPendingId(array $externalUserid,string $chatId = ''): mixed
    {
        $body = [
            'external_userid' => $externalUserid,
        ];

        !empty($chatId) && $body['chat_id'] = $chatId;

        $response = $this->getClient()->request('POST','cgi-bin/idconvert/batch/external_userid_to_pending_id',[
            'json' => $body
        ])->toArray(false);
        if($response['errcode'] != 0){
            return [];
        }

        return $response['result'];
    }

    /**
     * 获取客户群详情
     * @param string $chatId
     * @param int $needName
     * @return array
     * @throws \EasyWeChat\Kernel\Exceptions\BadResponseException
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
     */
    public function getExternalContactGroupChatDetail(string $chatId, int $needName = 0): array
    {
        $data = [
            'chat_id' => $chatId,
            'need_name' => $needName
        ];
        !empty($nextCursor) && $data['cursor'] = $nextCursor;

        $response = $this->getClient()->post('/cgi-bin/externalcontact/groupchat/get',[
            'json'=>$data
        ])->toArray(false);
        if($response['errcode'] != 0){
            logger()->info('获取客户群详情失败：'.$response['errmsg']);
            return [];
        }
        return $response['group_chat'];
    }

    public function getExternalContactGroupChatList( array $userId = [], int $statusFilter = 0, string $nextCursor = '', int $limit = 1000): array
    {
        $data = [
            'status_filter' => $statusFilter,
            'limit' => $limit
        ];

        if(!empty($userId)){
            $data['owner_filter'] = [
                'userid_list' => $userId
            ];
        }

        !empty($nextCursor) && $data['cursor'] = $nextCursor;
        $response = $this->getClient()->post('cgi-bin/externalcontact/groupchat/list' , [
            'json' => $data
        ])->toArray(false);
        if($response['errcode'] != 0){
            logger()->info('获取客户群列表失败：'.$response['errmsg']);
            return [];
        }
        return [
            'group_chat_list' => $response['group_chat_list'],
            'next_cursor' => $response['next_cursor'] ?? ''
        ];
    }

    /**
     * 修改客户信息
     * @param array $data
     * @return mixed
     */
    public function modifyExternalContactInfo(array $data): bool
    {
        $response = $this->getClient()->request('POST','cgi-bin/externalcontact/remark',[
            'json' => $data
        ])->toArray(false);
        if($response['errcode'] != 0){
            return false;
        }
        return true;
    }

    public function markTag(string $userid,string $externalUserid,array $add_tag,array $remove_tag)
    {
        $response = $this->getClient()->request('POST','cgi-bin/externalcontact/mark_tag',[
            'json' => [
                'userid' => $userid,
                'external_userid' => $externalUserid,
                'add_tag' => $add_tag,
                'remove_tag' => $remove_tag
            ]
        ])->toArray(false);
        if($response['errcode'] != 0){
            logger()->info('打标签失败：'.json_encode($response));
            $this->errMsg = $response['errmsg'];
            return false;
        }
        return true;
    }

    public function getCorpTagList(array $tagId = [],array $groupId = [])
    {
        $response = $this->getClient()->request('POST','cgi-bin/externalcontact/get_corp_tag_list',[
            'json' => [
                'tag_id' => $tagId,
                'group_id' => $groupId
            ]
        ])->toArray(false);
        if($response['errcode'] != 0){
            return [];
        }
        return $response['tag_group'];
    }

    public function addCorpTag(array $tag,string $groupId = '',string $groupName = '',mixed $order = '')
    {
        $data['tag'] = $tag;
        if(!empty($groupId)){
            $data['group_id'] = $groupId;
        }
        if(!empty($groupName)){
            $data['group_name'] = $groupName;
        }
        if(filled($order)){
            $data['order'] = $order;
        }

        $response = $this->getClient()->request('POST','cgi-bin/externalcontact/add_corp_tag',[
            'json' => $data
        ])->toArray(false);
        if($response['errcode'] != 0){
            if($response['errcode'] == 40071){
                $this->errMsg = '标签已存在';
            }else{
                $this->errMsg = $response['errmsg'];
            }
            return false;
        }
        return $response['tag_group'];
    }

    public function editCorpTag(string $id ,string $name = '',mixed $order = '')
    {
        $data['id'] = $id;
        if(!empty($name)){
            $data['name'] = $name;
        }

        if(filled($order)){
            $data['order'] = $order;
        }

        $response = $this->getClient()->request('POST','cgi-bin/externalcontact/edit_corp_tag',[
            'json' => $data
        ])->toArray(false);
        if($response['errcode'] != 0){
            if($response['errcode'] == 40071){
                $this->errMsg = '标签已存在';
            }else{
                $this->errMsg = $response['errmsg'];
            }
            return false;
        }
        return true;
    }

    public function delCorpTag(array $tagId,array $groupId)
    {
        $response = $this->getClient()->request('POST','cgi-bin/externalcontact/del_corp_tag',[
            'json' => [
                'tag_id' => $tagId,
                'group_id' => $groupId
            ]
        ])->toArray(false);
        if($response['errcode'] != 0){
            if($response['errcode'] == 81011){
                $this->errMsg = '非后台创建的标签，请在手机端进行删除操作';
            }else{
                $this->errMsg = $response['errmsg'];
            }
            return false;
        }
        return true;
    }

    /**
     * 激活账号
     * @param string $activeCode
     * @param string $corpid
     * @param string $userid
     * @return bool
     * @throws \EasyWeChat\Kernel\Exceptions\BadResponseException
     * @throws \Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface
     * @throws \Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface
     */
    public function activeAccount(string $activeCode,string $corpid,string $userid): bool
    {
        $response = $this->getClient()->request('POST','/cgi-bin/license/active_account',[
            'json' => [
                'active_code' => $activeCode,
                'corpid' => $corpid,
                'userid' => $userid
            ]
        ])->toArray(false);
        if($response['errcode'] == 0){
            return true;
        }
        $this->errMsg = $response['errmsg'];
        return false;
    }



    public function getNewExternalUserid(array $externalUseridList)
    {
        $response = $this->getClient()->post('/cgi-bin/externalcontact/get_new_external_userid',[
            'json'=>[
                'external_userid_list' => $externalUseridList
            ]
        ])->toArray(false);
        if($response['errcode'] != 0){
            logger()->info('获取客户群详情失败：'.$response['errmsg']);
            return [];
        }
        return $response['items'];
    }

    public function getConvertToUserid(mixed $openId)
    {
        $response = $this->getClient()->post('/cgi-bin/user/convert_to_userid',[
            'json'=>[
                'openid' => $openId
            ]
        ])->toArray(false);
        if($response['errcode'] != 0){
            logger()->info('获取openid转userid失败：'.$response['errmsg']);
            return [];
        }
        return $response['userid'];
    }

    public function addContactWay(AddContactWayDto $data): mixed
    {
        $data = $data->toArray();
        $data = array_filter($data,function ($item){
            return !empty($item);
        });
        $data['skip_verify']=!empty($data['skip_verify'])?true:false;
        logger()->info('渠道码客户联系入参',$data);
        $response = $this->getClient()->post('cgi-bin/externalcontact/add_contact_way' , [
            'json' => $data
        ])->toArray(false);
        if($response['errcode'] != 0){
            logger()->info('渠道码客户联系发送失败：'.$response['errmsg']);
            throw new NormalStatusException('企业微信创建渠道码客户联系发送失败'.$response['errmsg']);
            //return [];
        }
        return $response;
    }


    public function updateContactWay(AddContactWayDto $data): mixed
    {
        $data = $data->toArray();
        $data = array_filter($data,function ($item){
            return !empty($item);
        });
        $data['skip_verify']=!empty($data['skip_verify'])?true:false;
        logger()->info('修改渠道码客户联系入参',$data);
        $response = $this->getClient()->post('cgi-bin/externalcontact/update_contact_way' , [
            'json' => $data
        ])->toArray(false);
        if($response['errcode'] != 0){
            logger()->info('修改渠道码客户联系发送失败：'.$response['errmsg']);
            throw new NormalStatusException('修改企业微信创建渠道码客户联系发送失败'.$response['errmsg']);
            //return [];
        }
        return $response;
    }

    public function deleteContactWay($data): mixed
    {
        $data = array_filter($data,function ($item){
            return !empty($item);
        });
        logger()->info('删除渠道码客户联系入参',$data);
        $response = $this->getClient()->post('cgi-bin/externalcontact/del_contact_way' , [
            'json' => $data
        ])->toArray(false);
        if($response['errcode'] != 0){
            logger()->info('删除渠道码客户联系发送失败：'.$response['errmsg']);
            throw new NormalStatusException('删除企业微信创建渠道码客户联系失败'.$response['errmsg']);
            //return [];
        }
        return $response;
    }

    /**
     * 创建企业群发
     * @param AddMsgTemplateDto $data
     * @return array|mixed[]
     */
    public function addMsgTemplate(AddMsgTemplateDto $data): array
    {
        $data = $data->toArray();

        $data = array_filter($data,function ($item){
            return !empty($item);
        });
        logger()->info('创建企业群发入参',$data->toArray());
        $response = $this->getClient()->post('cgi-bin/externalcontact/add_msg_template' , [
            'json' => $data
        ])->toArray(false);
        logger()->info('创建企业群发发送失败发送结果：',$response);
        if($response['errcode'] != 0){
            logger()->info('创建企业群发发送失败：'.$response['errmsg']);
            return [];
        }
        return $response;
    }

    /**
     * 创建企业群发
     * @param AddMsgTemplateDto $data
     * @return array|mixed[]
     */
    public function uploadMedia($file,$type): array
    {
        $key = 'speed_media_upload:'.md5($file).'_'.$type;
        $result = redis()->get($key);
        if($result){
            return json_decode($result);
        }
        $response = $this->getClient()->post('cgi-bin/media/upload?type='.$type , [
            'multipart' => [
                [
                    'name'     => 'media',             // 这里指定文件字段的名称
                    'contents' => fopen($file, 'r'), // 读取文件
                    'filename' => time().rand(1000,9999),             // 上传文件的名称
                    'headers'  => [
                        'Content-Type' => 'application/octet-stream',  // 可选，设置文件的内容类型
                    ],
                ]
            ]
        ])->toArray(false);
        if($response['errcode'] != 0){
            logger()->info('极速群发上传文件失败：'.$response['errmsg']);
            return [];
        }
        redis()->set($key,json_encode($response),3 * 86400);
        return $response;
    }

    public function requestGet(string $url,array $data = []): array
    {
        $response = $this->getClient()->request('GET', $url, [
            'query' => $data,
        ])->toArray(false);
        if(isset($response['errcode']) && $response['errcode'] !== 0){
            logger()->info($url.'请求失败，错误信息：'.$response['errmsg']);
            return [];
        }
        return $response;
    }


    public function requestPost($url,$data): array
    {
        $response = $this->getClient()->request('POST', $url, [
            'json' => $data,
        ])->toArray(false);
        if(isset($response['errcode']) && $response['errcode'] !== 0){
            logger()->info($url.'请求失败，错误信息：'.$response['errmsg']);
            throw new NormalStatusException($url.'请求失败，错误信息：'.$response['errmsg']);
        }
        return $response;
    }

    /**
     * @return array<string, mixed>
     */
    protected function getHttpClientDefaultOptions(): array
    {
        return array_merge(
            ['base_uri' => 'https://qyapi.weixin.qq.com/'],
            (array) $this->config->get('http', [])
        );
    }
}